#coding=utf-8
"""
@author : leecrest
@time   : 14-3-1 下午7:51
@brief  : prof日志查看器
"""

from PyQt4 import QtGui
import sys
import os
import marshal
import Resource

APP_TITLE = u"Prof文件查看器"
FILE_EXT = ".prof"
HEADER_DICT = {
	"name"      : {"idx":0, "text":u"函数名称"},
	"count"     : {"idx":1, "text":u"调用次数"},
    "time"      : {"idx":2, "text":u"总耗时[ms]"},
	"avg"       : {"idx":3, "text":u"平均耗时[ms]"},
	"percent"   : {"idx":4, "text":u"百分比"},
	"file"      : {"idx":5, "text":u"文件", "visible":False},
	"line"      : {"idx":6, "text":u"行号", "visible":False},
}
HEADER_LIST = ("name", "count", "time", "avg", "percent", "file", "line")
ACTION_CONFIG = {
	"autosize"  : {"text":u"自动列宽", "check":True},
	"collapse"  : {"text":u"全部收缩",},
	"expand"    : {"text":u"全部展开",},
    "count"     : {"text":u"调用次数", "check":True},
    "time"      : {"text":u"总耗时", "check":True},
    "avg"       : {"text":u"平均耗时", "check":True},
    "percent"   : {"text":u"百分比", "check":True},
    "file"      : {"text":u"文件", "check":False},
    "line"      : {"text":u"行号", "check":False},
}
POP_MENU_CONFIG = (
	"autosize", "collapse", "expand",
	"Separator",
	"count", "time", "avg", "percent", "file", "line"
)



class CMainFrame(QtGui.QMainWindow):
	def __init__(self):
		super(CMainFrame, self).__init__()
		self.setWindowIcon(QtGui.QIcon(":/profviewer"))
		self.setWindowTitle(APP_TITLE)
		self.resize(700, 400)
		self.InitUI()
		self.InitMenu()
		self.m_File = ""
		self.m_Data = None


	def InitUI(self):
		self.m_TreeCtrl = QtGui.QTreeWidget()
		self.setCentralWidget(self.m_TreeCtrl)
		self.m_TreeCtrl.setColumnCount(len(HEADER_LIST))
		sNameList = []
		bHideList = []
		for sName in HEADER_LIST:
			dHeader = HEADER_DICT[sName]
			sNameList.append(dHeader["text"])
			if not dHeader.get("visible", True):
				bHideList.append(dHeader["idx"])
		self.m_TreeCtrl.setHeaderLabels(sNameList)
		self.m_TreeCtrl.setColumnWidth(0, 200)
		for idx in bHideList:
			self.m_TreeCtrl.header().setSectionHidden(idx, True)
		self.OnAutoSize(True)
		self.setAcceptDrops(True)


	def InitMenu(self):
		#初始化Action
		self.m_ActionDict = {}
		for sName, dAction in ACTION_CONFIG.iteritems():
			sText = dAction.get("text", None)
			if not sText:
				continue
			action = QtGui.QAction(sText, self)
			sIcon = dAction.get("icon", None)
			if sIcon:
				action.setIcon(QtGui.QIcon(sIcon))
			if "check" in dAction:
				action.setCheckable(True)
				action.setChecked(dAction["check"])
			action.setObjectName(sName)
			action.triggered.connect(self.OnAction)
			self.m_ActionDict[sName] = action

		#初始化菜单
		self.m_PopMenu = QtGui.QMenu()
		for sName in POP_MENU_CONFIG:
			if sName == "Separator":
				self.m_PopMenu.addSeparator()
				continue
			if not sName in self.m_ActionDict:
				continue
			action = self.m_ActionDict[sName]
			self.m_PopMenu.addAction(action)

	def OnAction(self, bChecked):
		sName = self.sender().objectName()
		if sName == "autosize":
			self.OnAutoSize(bChecked)
		elif sName == "collapse":
			self.m_TreeCtrl.collapseAll()
		elif sName == "expand":
			self.m_TreeCtrl.expandAll()
		elif sName in HEADER_LIST:
			idx = HEADER_LIST.index(sName)
			self.m_TreeCtrl.header().setSectionHidden(idx, not bChecked)

	def OnAutoSize(self, bChecked):
		if bChecked:
			self.m_TreeCtrl.header().setResizeMode(QtGui.QHeaderView.ResizeToContents)
			#self.m_TreeCtrl.header().setStretchLastSection(True)
		else:
			self.m_TreeCtrl.header().setResizeMode(QtGui.QHeaderView.Interactive)

	@staticmethod
	def GetDropFile(event):
		data = event.mimeData()
		sFileList = data.urls()
		if not sFileList:
			return
		sFile = unicode(sFileList[0].toString())
		sFile = sFile[len("file:///"):]
		sName, sExt = os.path.splitext(os.path.basename(sFile))
		if sExt.lower() != FILE_EXT:
			return
		return sFile


	def dragEnterEvent(self, event):
		if self.GetDropFile(event):
			event.acceptProposedAction()


	def dropEvent(self, event):
		sFile = self.GetDropFile(event)
		if not sFile:
			return
		self.LoadFile(sFile)


	def LoadFile(self, sFilePath):
		if not sFilePath:
			return
		hFile = open(sFilePath, "rb")
		dList = marshal.load(hFile)
		hFile.close()
		if not dList:
			return
		self.m_TreeCtrl.clear()
		for dNode in dList:
			if not self.LoadNode(dNode):
				self.LoadFile(self.m_File)
				return
		self.m_Data = dList
		self.m_File = sFilePath
		self.setWindowTitle("%s - [%s]" % (APP_TITLE, sFilePath))
		self.m_TreeCtrl.expandAll()


	def LoadNode(self, dNode, oParent=None):
		bRoot = False
		if not oParent:
			oParent = self.m_TreeCtrl
			bRoot = True
		oNode = QtGui.QTreeWidgetItem(oParent)
		for sName, dHeader in HEADER_DICT.iteritems():
			if not sName in dNode:
				return False
			sValue = dNode[sName]
			if sName in ("time", "avg"):
				sValue = "%6f" % (sValue*1000) #换算成毫秒
			elif sName == "percent":
				sValue = "%.2f%%" % sValue
			elif not isinstance(sValue, str):
				sValue = str(sValue)
			oNode.setText(dHeader["idx"], sValue)
		if "child" in dNode:
			for dNodeData in dNode["child"]:
				if not self.LoadNode(dNodeData, oNode):
					return False
		if bRoot:
			self.m_TreeCtrl.addTopLevelItem(oNode)
		return True


	def contextMenuEvent(self, event):
		self.m_PopMenu.exec_(event.globalPos())

if __name__ == "__main__":
	app = QtGui.QApplication(sys.argv)
	mainWindow = CMainFrame()
	mainWindow.show()
	sys.exit(app.exec_())
